﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

using ApproveFlowEngine.Approve.DTO;
using ApproveFlowEngine.Core;
using ApproveFlowEngine.Core.Base;
using ApproveFlowEngine.Core.Nodes;
using ApproveFlowEngine.Enums.Core;

using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Domain.Repositories;

namespace ApproveFlowEngine.Approve
{
    public class ApproveAppService : ApproveFlowEngineAppService, IApproveAppService
    {
        #region Private Fields

        private readonly WorkFlowDomainService _workFlowDomainService;
        private readonly IRepository<WorkFlow> _workFlowRepository;
        private readonly IRepository<WorkNodeSerial> _workNodeRepository;

        #endregion Private Fields

        #region Public Constructors

        public ApproveAppService(WorkFlowDomainService workFlowDomainService, IRepository<WorkFlow> workFlowRepository, IRepository<WorkNodeSerial> workNodeRepository)
        {
            _workFlowDomainService = workFlowDomainService;
            _workFlowRepository = workFlowRepository;
            _workNodeRepository = workNodeRepository;
        }

        #endregion Public Constructors



        #region Public Methods

        public async Task Create(WorkFlowDto input)
        {
            var flow = await _workFlowDomainService.Create(input.FlowNo, input.FlowName, input.Remark, input.ApproveType, input.IsDraft ? ApproveFlowStatus.Draft : ApproveFlowStatus.None);

            BuildNode(flow, input.NodeTree, flow.Root);
            await _workFlowRepository.InsertAsync(flow);
        }

        public async Task FlowApprove(Guid flowId,Guid nodeId,ApproveNodeStatus nodeStatus)
        {
            var flow = await _workFlowRepository.FindAsync(c => c.Id == flowId);
            
            if (flow is null)
            {
                throw new BusinessException($"flow '{flowId}' is not find!");
            }
            flow.ExecApprove(nodeId,nodeStatus);
        }
        public async Task FlowStart(Guid flowId)
        {
            var flow = await _workFlowRepository.FindAsync(c => c.Id == flowId);

            if (flow is null)
            {
                throw new BusinessException($"flow '{flowId}' is not find!");
            }
            flow.BuildTree();
            flow.Start();
        }

        public async Task<PagedResultDto<WorkFlowDto>> GetFlowPage(PagedResultRequestDto input)
        {
            var result = new PagedResultDto<WorkFlowDto>();
            var data = await _workFlowRepository.GetPagedListAsync(input.SkipCount, input.MaxResultCount, nameof(WorkFlow.Id), true);

            result.Items = ObjectMapper.Map<List<WorkFlow>, List<WorkFlowDto>>(data);
            foreach (var item in result.Items)
            {
                item.InitTree();
            }
            result.TotalCount = await _workFlowRepository.GetCountAsync();
            return result;
        }

        public async Task Update(Guid flowId, WorkFlowDto input)
        {
            var flow = await _workFlowRepository.FindAsync(c => c.Id == flowId, true, default);

            if (flow is null)
            {
                throw new UserFriendlyException("审批单不存在！");
            }
            if (flow.FlowStatus is ApproveFlowStatus.Activity)
            {
                throw new UserFriendlyException("流程已开始！");
            }
            flow.ReSetNode();
            BuildNode(flow, input.NodeTree, flow.Root);
            flow.FlowName = input.FlowName;
            flow.Remark = input.Remark;
            flow.ApproveType = ApproveType.None;
            await _workFlowRepository.UpdateAsync(flow);
        }
        public async Task Delete(Guid flowId)
        {
            var flow = await _workFlowRepository.FindAsync(c => c.Id == flowId, true, default);
            if (flow is not null)
            {
                await _workFlowRepository.DeleteAsync(flow);
            }
        }

        #endregion Public Methods



        #region Private Methods

        private WorkNodeComplex BuildNode(WorkFlow flow, List<WorkNodeDto> tree, WorkNodeComplex nodeComplex)
        {
            foreach (var node in tree)
            {
                if (node.NodeType is NodeType.Simple)
                {
                    var workNode = _workFlowDomainService.CreateSimple(flow);
                    workNode.SetParent(nodeComplex);
                    flow.AddNode(workNode);
                }
                else
                {
                    if (node.ParentId is not null)
                    {
                        var tempNode = _workFlowDomainService.CreateComplexNode(flow,node.NodeType);
                        tempNode.SetParent(nodeComplex);
                        flow.AddNode(tempNode);
                        BuildNode(flow, node.Children, tempNode);
                    }
                    else
                    {
                        BuildNode(flow, node.Children, nodeComplex);
                    }
                }
            }

            return nodeComplex;
        }

        #endregion Private Methods
    }
}